from pymongo import Connection, uri_parser
from model.scrabble import Player, Game
from model.utils import create_rand_letters_object, RandomGameLetters

import logging
logger = logging.getLogger("dabble")


class BaseRepository(object):
    def __init__(self):
        pass

    def read(self, uuid):
        raise NotImplementedError("Missing function")

    def read_all(self):
        raise NotImplementedError("Missing function")

    def count(self):
        raise NotImplementedError("Missing function")


class MongoPlayerRepository(BaseRepository):
    def __init__(self, uri="mongodb://localhost/scrabble", db_name="scrabble"):
        # Try to extract the database name from the uri
        split_uri = uri_parser.parse_uri(uri)
        self.db_name = split_uri.get("database", db_name)

        # Set up the connection (using localhost, and default port)
        self.connection = Connection(host=uri)
        # Get the database
        self.db = self.connection[self.db_name]
        # Get the collection
        self.players = self.db.players

    def drop_repository(self):
        self.connection.drop_database(self.db.name)

    def create(self, player):
        if self.players.find_one({ "uuid": player.uuid }) or self.players.find_one({ "mail": player.mail }):
            raise Exception("Player already exists (uuid=%s, mail=%s)." % (player.uuid, player.mail))

        self.players.insert( { "name": player.name,
                               "uuid": player.uuid,
                               "mail": player.mail} )

    def read(self, uuid):
        player_info = self.players.find_one( { "uuid": uuid } )
        if not player_info:
            raise Exception("No such player (uuid=%s)." % uuid)

        return Player(uuid, player_info["name"], player_info["mail"])

    def read_all(self):
        player_infos = self.players.find()
        return [ Player(p["uuid"], p["name"], p["mail"]) for p in player_infos ]

    def count(self):
        return self.players.count()


class MockPlayerRepository(BaseRepository):
    def __init__(self):
        import uuid
        self._names = [u"John Doe", u"Jane Doe", u"Kong Harald"]
        self._mails = ["john.doe@norway.no", "jane@jameson.no", "king@kongehuset.no"]
        self.players = [Player(str(uuid.uuid4()),n,e) for n,e in zip(self._names, self._mails)]
        logger.debug("Starting 'MockPlayerRepository.")

    def read(self, uuid):
        for player in self.players:
            if player.uuid == uuid:
                return player

        return None

    def read_all(self):
        return self.players

    def create(self, player):
        if player in self.players:
            msg = "Player (uuid=%s, name=%s, mail=%s) already exists." % (player.uuid, player.name, player.mail)
            logger.debug(msg)
            raise Exception(msg)

        logger.debug("Adding player (uuid=%s, name=%s, mail=%s)" % (player.uuid, player.name, player.mail))
        self.players.append(player)


class MockGameRepository(BaseRepository):
    def __init__(self):
        self.games = {}

    def read(self, uuid):
        return self.games.get(uuid, None)

    def read_all(self):
        return self.games.values()

    def create(self, game):
        if game in self.games:
            raise Exception("Game already exists (uuid=%s)." % game.uuid)

        logger.debug("Adding game (uuid=%s)" % game.uuid)

        player1 = game.players[0]
        player2 = game.players[1]
        self.games[game.uuid] = dict()

class MongoGameRepository(BaseRepository):
    def __init__(self, uri="mongodb://localhost/scrabble", db_name="scrabble"):
        # Try to extract the database name from the uri
        split_uri = uri_parser.parse_uri(uri)
        self.db_name = split_uri.get("database", db_name)

        # Set up the connection (using localhost, and default port)
        self.connection = Connection(host=uri)
        # Get the database
        self.db = self.connection[self.db_name]
        # Get the collection
        self.games = self.db.games

    def create(self, game):
        player1 = game.players[0]
        player1_random_game_letter_object = game.get_user_randomgameletters(player1.uuid)

        player2 = game.players[1]
        player2_random_game_letter_object = game.get_user_randomgameletters(player2.uuid)

        self.games.insert( { "uuid": game.uuid,
                             "player1": { "uuid" : player1.uuid,
                                          "name" : player1.name,
                                          "mail" : player1.mail,
                                          "letters" : player1_random_game_letter_object.get_base_object()},
                             "player2": { "uuid" : player2.uuid,
                                          "mail" : player2.mail,
                                          "name" : player2.name,
                                          "letters" : player2_random_game_letter_object.get_base_object()}
                             } )

    def read(self, uuid):
        game_info = self.games.find_one( { "uuid": uuid } )
        logger.debug("Game info: %s" % game_info)
        return MongoGameRepository.create_from_dict(game_info)

    @staticmethod
    def create_from_dict(game_info):
        player1 = game_info["player1"]
        player2 = game_info["player2"]

        players = [ Player(player1["uuid"], player1["name"], player1["mail"]),
                    Player(player2["uuid"], player2["name"], player2["mail"]) ]

        player1_letters = RandomGameLetters(player1["letters"])
        player2_letters = RandomGameLetters(player2["letters"])
        players_letters = [player1_letters, player2_letters]

        return Game(players, game_info["uuid"], players_letters)

    def read_all(self):
        game_infos = self.games.find()
        return [ MongoGameRepository.create_from_dict(g) for g in game_infos ]

    def read_all_for_player(self, player_uuid):
        game_infos = self.games.find( { "$or": [ { "player1.uuid": player_uuid },
                                                 { "player2.uuid": player_uuid }] })
        return [ MongoGameRepository.create_from_dict(g) for g in game_infos ]

    def drop_repository(self):
        self.connection.drop_database(self.db.name)

    def count(self):
        return self.games.count()

